package com.example.demo.T2.service;

import com.example.demo.T2.pojo.Car;
import com.example.demo.T2.pojo.QueueInfo;
import com.example.demo.T2.pojo.TollType;
import com.example.demo.T2.util.FakeTimer;
import com.example.demo.T2.util.TollCheckTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

/**
 * Service..
 *
 * @author Lizhong
 * @date 2020/8/18
 */
@org.springframework.stereotype.Service
public class TollService {
    static Logger LOGGER = LoggerFactory.getLogger(TollService.class.getName());

    /**
     * 进入收费站
     */
    Queue<Car> globalWaitingQueue = new ConcurrentLinkedQueue<>();
    /**
     * 离开收费站
     */
    Queue<Car> globalDeliverQueue = new ConcurrentLinkedQueue<>();

    /**
     * 全局读写锁条件
     */
    ReentrantReadWriteLock globalLock = new ReentrantReadWriteLock(true);
    ReentrantReadWriteLock.ReadLock readLock = globalLock.readLock();
    ReentrantReadWriteLock.WriteLock writeLock = globalLock.writeLock();

    /**
     * 所有工作线程
     */
    List<TollWorker> etcList = new ArrayList<>();
    List<TollWorker> manuelList = new ArrayList<>();

    /**
     * 用于中断正在收费的线程
     */
    List<Thread> threads = new ArrayList<>();


    /**
     * 通道数量
     */
    static int ETC_NUM = 3;
    static int MANUAL_NUM = 1;
    static int MIXED_NUM = 1;

    /**
     * 等待时间
     */
    static int ETC_MIN_MINUTES = 10;
    static int ETC_MAX_MINUTES = 20;

    static int MANUAL_MIN_MINUTES = 15;
    static int MANUAL_MAX_MINUTES = 25;

    public FakeTimer fakeTimer = new FakeTimer(0, 60 * 60, 10, 30);

    /**
     * 获取收费站的过路信息
     *
     * @return
     */
    public QueueInfo getQueueInfo() {
        readLock.lock();
        try {
            Map<String, Queue<Car>> mergeMap = etcList.stream().collect(Collectors.toMap(TollWorker::getName, TollWorker::getWaitingQueue));
            for (TollWorker tollWorker : manuelList) {
                mergeMap.putIfAbsent(tollWorker.getName(), tollWorker.getWaitingQueue());
            }
            // 按名字排序
            LinkedHashMap<String, Queue<Car>> orderMap = new LinkedHashMap<>();
            mergeMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> orderMap.put(e.getKey(), e.getValue()));

            QueueInfo queueInfo = new QueueInfo();
            queueInfo.setTollWindow(orderMap);
            queueInfo.setGlobalDeliverQueue(new ArrayList<>(globalDeliverQueue));
            queueInfo.setGlobalWaitingQueue(new ArrayList<>(globalWaitingQueue));
            return queueInfo;
        } finally {
            readLock.unlock();
        }
    }

    /**
     * 初始化线程数量，随机数策略
     */
    @PostConstruct
    public void init() {
        // 初始化两种RandomTimer
        TollCheckTime etcWaitTimer = new TollCheckTime(ETC_MIN_MINUTES, ETC_MAX_MINUTES);
        TollCheckTime manuelStrategy = new TollCheckTime(MANUAL_MIN_MINUTES, MANUAL_MAX_MINUTES);

        // 每个窗口有哪些计时策略
        Map<String, TollCheckTime> etcWaitMap = new HashMap<>();
        etcWaitMap.put(TollType.ETC, etcWaitTimer);

        Map<String, TollCheckTime> manuelWaitMap = new HashMap<>();
        manuelWaitMap.put(TollType.MANUAL, manuelStrategy);

        Map<String, TollCheckTime> mixedWaitMap = new HashMap<>();
        mixedWaitMap.put(TollType.ETC, etcWaitTimer);
        mixedWaitMap.put(TollType.MANUAL, manuelStrategy);

        // 构建线程
        for (int i = 0; i < ETC_NUM; i++) {
            TollWorker target = new TollWorker("ETC-" + i, fakeTimer, new LinkedList<>(), etcWaitMap, globalLock, globalWaitingQueue, globalDeliverQueue);
            Thread thread = new Thread(target);
            etcList.add(target);
            threads.add(thread);
            thread.start();
        }

        for (int i = 0; i < MANUAL_NUM; i++) {
            TollWorker target = new TollWorker("MANUAL-" + i, fakeTimer, new LinkedList<>(), manuelWaitMap, globalLock, globalWaitingQueue, globalDeliverQueue);
            Thread thread = new Thread(target);
            manuelList.add(target);
            threads.add(thread);
            thread.start();
        }

        for (int i = 0; i < MIXED_NUM; i++) {
            TollWorker target = new TollWorker("MIXED-" + i, fakeTimer, new LinkedList<>(), mixedWaitMap, globalLock, globalWaitingQueue, globalDeliverQueue);
            Thread thread = new Thread(target);
            etcList.add(target);
            manuelList.add(target);
            threads.add(thread);
            thread.start();
        }


    }

    /**
     * 提供服务
     *
     * @param car
     */
    public void service(Car car) {


        TollWorker tollWorker = null;
        // 对数组排序，禁止其他线程过来排序
        writeLock.lock();
        try {
            if (car.getCarType().equals(TollType.ETC)) {
                etcList.sort(Comparator.comparingLong(TollWorker::getTotalWaitingTime));
                tollWorker = etcList.get(0);

            } else {
                manuelList.sort(Comparator.comparingLong(TollWorker::getTotalWaitingTime));
                tollWorker = manuelList.get(0);
            }
            // 添加任务
            tollWorker.add(car);
            car.setEtcAt(tollWorker.getName());
        } finally {
            writeLock.unlock();
        }
    }


    /**
     * 清空队列，中断线程
     */
    public void clear() {
        writeLock.lock();
        try {
            globalWaitingQueue.clear();
            globalDeliverQueue.clear();
            etcList.forEach(TollWorker::clear);
            manuelList.forEach(TollWorker::clear);
            threads.forEach(Thread::interrupt);
        } finally {
            writeLock.unlock();
        }
    }
}

